#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{		
		ref class DVector3;
		ref class DBody;

		///<summary>
		/// The world object is a container for rigid bodies and joints. Objects in
		/// different worlds can not interact, for example rigid bodies from two
		/// different worlds can not collide.
		///
		/// All the objects in a world exist at the same point in time, thus one
		/// reason to use separate worlds is to simulate systems at different rates.
		/// Most applications will only need one world.
		///</summary>
		public ref class DWorld : DIUnmanagedPointer
		{
			INTERNAL_CLASS_DECLARATION_NO_FINALIZE(DWorld,dxWorld);
		private:
			Dictionary<GeomKey,DBody^>^ _Bodies;
		internal:
			void AddBody(DBody^ b);
			void RemoveBody(DBody^ b);			
		public:

			///<summary>
			/// Destroy a world and everything in it.
			///
			// This includes all bodies, and all joints that are not part of a joint
			// group. Joints that are part of a joint group will be deactivated, and
			// can be destroyed by calling, for example, dJointGroupEmpty().						
			///</summary>
			!DWorld();

			UNMANAGEDPOINTER_DECLARATION();			

			///<summary>
			/// Create a new, empty world and return its ID number.
			///</summary>
			DWorld()
			{
				_SetPointer(dWorldCreate(),true);
				_Bodies = gcnew Dictionary<GeomKey,DBody^>();
			}

			///<summary>
			/// Gets or set the gravity vector for a given world.
			/// The units are m/s^2, so Earth's gravity vector would be (0,0,-9.81),
			/// assuming that +z is up. The default is no gravity, i.e. (0,0,0).
			///</summary>
			REF_PROP_DECLARATION_GETSET(DVector3,Gravity);			
			
			///<summary>
			/// Gets or set the global ERP value, that controls how much error
			/// correction is performed in each time step.
			/// Typical values are in the range 0.1--0.8. The default is 0.2.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,ERP);
			
			///<summary>
			/// Gets or set the global CFM (constraint force mixing) value.
			/// Typical values are in the range @m{10^{-9}} -- 1.
			/// The default is 10^-5 if single precision is being used, or 10^-10
			/// if double precision is being used.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,CFM);


			///<summary>
			/// This uses a "big matrix" method that takes time on the order of m^3
			/// and memory on the order of m^2, where m is the total number of constraint
			/// rows. For large systems this will use a lot of memory and can be very slow,
			/// but this is currently the most accurate method.
			///</summary>			
			///<param name = "stepsize" > The number of seconds that the simulation has to advance.</param>			
			void Step(dReal stepsize);


			///<summary>
			/// Converts an impulse to a force.
			///</summary>
			///<remarks>			
			/// If you want to apply a linear or angular impulse to a rigid body,
			/// instead of a force or a torque, then you can use this function to convert
			/// the desired impulse into a force/torque vector before calling the
			/// BodyAdd... function.
			/// The current algorithm simply scales the impulse by 1/stepsize,
			/// where stepsize is the step size for the next step that will be taken.
			/// This function is given a dWorldID because, in the future, the force
			/// computation may depend on integrator parameters that are set as
			/// properties of the world.
			///</remarks>
			void ImpulseToForce(dReal stepsize,dReal ix, dReal iy, dReal iz, DVector3^ Force);

			///<summary>
			/// Step the world.
			///</summary>
			///<remarks>
			/// This uses an iterative method that takes time on the order of m*N
			/// and memory on the order of m, where m is the total number of constraint
			/// rows N is the number of iterations.
			/// For large systems this is a lot faster than dWorldStep(),
			/// but it is less accurate.
			///
			/// QuickStep is great for stacks of objects especially when the
			/// auto-disable feature is used as well.
			/// However, it has poor accuracy for near-singular systems.
			/// Near-singular systems can occur when using high-friction contacts, motors,
			/// or certain articulated structures. For example, a robot with multiple legs
			/// sitting on the ground may be near-singular.			
			///
			/// There are ways to help overcome QuickStep's inaccuracy problems:
			/// \li Increase CFM.
			/// \li Reduce the number of contacts in your system (e.g. use the minimum
			///     number of contacts for the feet of a robot or creature).
			/// \li Don't use excessive friction in the contacts.
			/// \li Use contact slip if appropriate
			/// \li Avoid kinematic loops (however, kinematic loops are inevitable in
			///     legged creatures).
			/// \li Don't use excessive motor strength.
			/// \liUse force-based motors instead of velocity-based motors.
			///
			/// Increasing the number of QuickStep iterations may help a little bit, but
			/// it is not going to help much if your system is really near singular.
			///</remarks>
			void QuickStep(dReal stepsize);


			///<summary>
			/// Gets or set the number of iterations that the QuickStep method performs per
			/// step. The default is 20 iterations.
			///</summary>
			///<remarks>
			/// More iterations will give a more accurate solution, but will take
			/// longer to compute.			
			///</remarks>			
			VALUE_PROP_DECLARATION_GETSET(int,QuickStepNumIterations);						

			///<summary>
			/// Gets or set the SOR over-relaxation parameter
			///</summary>			
			VALUE_PROP_DECLARATION_GETSET(dReal,QuickStepW);								

			///<summary>
			/// Gets or set the maximum correcting velocity that contacts are allowed
			/// The default value is infinity (i.e. no limit).
			///</summary>
			///<remarks> Reducing this value can help prevent "popping" of deeply embedded objects.</remarks>			
			VALUE_PROP_DECLARATION_GETSET(dReal,ContactMaxCorrectingVelocity);
			
			///<summary>
			/// Gets or set the depth of the surface layer around all geometry objects.			
			///</summary>
			///<remarks> 
			/// Contacts are allowed to sink into the surface layer up to the given
			/// depth before coming to rest.
			/// Increasing this to some small value (e.g. 0.001) can help prevent
			/// jittering problems due to contacts being repeatedly made and broken.
			///</remarks>			
			VALUE_PROP_DECLARATION_GETSET(dReal,ContactSurfaceLayer);			
	
			///<summary>
			/// Step the world using the StepFast1 algorithm.
			///</summary>
			///<param name = "stepsize"> the nr of seconds to advance the simulation.</param>
			///<param name = "maxiterations"> The number of iterations to perform.</param>			
			void StepFast1(dReal stepsize, int maxiterations);


			///<remarks>			
			/// Every body can be enabled or disabled. Enabled bodies participate in the
			/// simulation, while disabled bodies are turned off and do not get updated
			/// during a simulation step. New bodies are always created in the enabled state.
			///
			/// A disabled body that is connected through a joint to an enabled body will be
			/// automatically re-enabled at the next simulation step.
			///
			/// Disabled bodies do not consume CPU time, therefore to speed up the simulation
			/// bodies should be disabled when they come to rest. This can be done automatically
			/// with the auto-disable feature.
			///
			/// If a body has its auto-disable flag turned on, it will automatically disable
			/// itself when
			///   @li It has been idle for a given number of simulation steps.
			///   @li It has also been idle for a given amount of simulation time.
			///
			/// A body is considered to be idle when the magnitudes of both its
			/// linear average velocity and angular average velocity are below given thresholds.
			/// The sample size for the average defaults to one and can be disabled by setting
			/// to zero with 
			///
			/// Thus, every body has six auto-disable parameters: an enabled flag, a idle step
			/// count, an idle time, linear/angular average velocity thresholds, and the
			/// average samples count.
			///
			/// Newly created bodies get these parameters from world.
			///</remarks>			

			///<summary>
			/// Gets or set the AutoEnableDepth parameter used by the StepFast1 algorithm.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(int,AutoEnableDepthSF1);
			
			///<summary>
			/// Gets or set auto disable linear threshold for newly created bodies.
			///default is 0.01
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableLinearThreshold);

			///<summary>
			/// Gets or set auto disable angular threshold for newly created bodies.			
			/// default is 0.01
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableAngularThreshold);

			///<summary>
			/// Gets or set auto disable linear average threshold for newly created bodies.
			/// default is 0.01
			///</summary>
			//VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableLinearAverageThreshold);

			///<summary>
			/// Gets or set auto disable angular average threshold for newly created bodies.
			/// default is 0.01
			///</summary>
			//VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableAngularAverageThreshold);

			///<summary>
			/// Gets or set auto disable sample count for newly created bodies.
			/// Default is 1, meaning only instantaneous velocity is used.
			/// Set to zero to disable sampling and thus prevent any body from auto-disabling.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(int,AutoDisableAverageSamplesCount);			

			///<summary>
			/// Gets or set  auto disable steps for newly created bodies.
			/// default is 10
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(int,AutoDisableSteps);

			///<summary>
			/// Gets or set auto disable time for newly created bodies.
			/// default is 0 seconds
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,AutoDisableTime);

			///<summary>
			/// Gets or set auto disable flag for newly created bodies.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(bool,AutoDisableFlag);


			///<summary>			
			/// Damping serves two purposes: reduce simulation instability, and to allow
			/// the bodies to come to rest (and possibly auto-disabling them).
			///
			/// Bodies are constructed using the world's current damping parameters. Setting
			/// the scales to 0 disables the damping.
			///
			/// Here is how it is done: after every time step linear and angular
			/// velocities are tested against the corresponding thresholds. If they
			/// are above, they are multiplied by (1 - scale). So a negative scale value
			/// will actually increase the speed, and values greater than one will
			/// make the object oscillate every step; both can make the simulation unstable.
			///
			/// To disable damping just set the damping scale to zero.
			///
			/// You can also limit the maximum angular velocity. In contrast to the damping
			/// functions, the angular velocity is affected before the body is moved.
			/// This means that it will introduce errors in joints that are forcing the body
			/// to rotate too fast. Some bodies have naturally high angular velocities
			/// (like cars' wheels), so you may want to give them a very high (like the default,
			/// dInfinity) limit.
			///
			/// @note The velocities are damped after the stepper function has moved the
			/// object. Otherwise the damping could introduce errors in joints. First the
			/// joint constraints are processed by the stepper (moving the body), then
			/// the damping is applied.
			///
			/// @note The damping happens right after the moved callback is called; this way 
			/// it still possible use the exact velocities the body has acquired during the
			/// step. You can even use the callback to create your own customized damping.
			///</summary>

			///<summary>
			/// Gets or set the world's linear damping threshold.			
			/// The damping won't be applied if the linear speed is
			/// below this threshold. Default is 0.01.			
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,LinearDampingThreshold);

			///<summary>
			/// Gets or set the world's angular damping threshold.
			/// The damping won't be applied if the angular speed is
			/// below this threshold. Default is 0.01.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,AngularDampingThreshold);

			///<summary>
			/// Gets or set the world's linear damping scale.
			/// The linear damping scale that is to be applied to bodies.
			///  Default is 0 (no damping). Should be in the interval [0, 1].
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,LinearDamping);

			///<summary>
			/// Gets or set the world's angular damping scale.
			/// The angular damping scale that is to be applied to bodies.
			/// Default is 0 (no damping). Should be in the interval [0, 1].
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,AngularDamping);

			///<summary>
			/// Convenience function to set body linear and angular scales.
			///</summary>
			///<param name = "linear_scale" > The linear damping scale that is to be applied to bodies. </param>
			///<param name = "angular_scale" > The angular damping scale that is to be applied to bodies.</param>			
			void SetDamping(dReal linear_scale,dReal angular_scale);

			///<summary>
			/// Gets or set the default maximum angular speed.
			///</summary>
			VALUE_PROP_DECLARATION_GETSET(dReal,MaxAngularSpeed); 

		};		
	}
}